#include "config.h"

#include "ecm.h"

#ifdef _WIN32

#include <windows.h>

#endif

#define SIGNO_MAX	2

static ecm_mutex_t	sig_mutex;
static ecm_sighandler_t	handler_table[SIGNO_MAX];

#ifndef _WIN32

static void
signal_handler(int signum)
{
	ecm_sighandler_t	handler = NULL;
	ecm_signo_t	signo = 0;

	ecm_mutex_lock(sig_mutex);
	switch (signum) {
	case SIGINT:
		signo = ECM_SIGINT;
		break;
	case SIGQUIT:
	case SIGTERM:
		signo = ECM_SIGTERM;
		break;
	default:
		break;
	}
	if (signo > 0)
		handler = handler_table[signo - 1];
	ecm_mutex_unlock(sig_mutex);

	if (handler)
		handler(signo);
}

static void
sigusr2_handler(int signum)
{
	extern void on_thread_sigusr2_handled(void);

	on_thread_sigusr2_handled();
}

#else

static BOOL WINAPI
CtrlHandler(DWORD dwCtrlType)
{
	ecm_sighandler_t	handler = NULL;
	ecm_signo_t	signo = 0;

	ecm_mutex_lock(sig_mutex);
	switch (dwCtrlType) {
		case CTRL_C_EVENT:
			signo = ECM_SIGINT;
			break;
		case CTRL_CLOSE_EVENT:
			signo = ECM_SIGTERM;
			break;
		default:
			break;
	}
	if (signo > 0)
		handler = handler_table[signo - 1];
	ecm_mutex_unlock(sig_mutex);

	if (handler == NULL)
		return FALSE;
	handler(signo);
	return TRUE;
}

#endif

ecm_sighandler_t
ecm_signal(ecm_signo_t signo, ecm_sighandler_t handler)
{
	ecm_sighandler_t	oldhandler = NULL;

	ecm_mutex_lock(sig_mutex);
	if (signo > 0 && signo <= SIGNO_MAX) {
		oldhandler = handler_table[signo - 1];
		handler_table[signo - 1] = handler;
	}
	ecm_mutex_unlock(sig_mutex);

	return oldhandler;
}

#ifndef _WIN32

static void
setup_handler(void)
{
	struct sigaction	act;

	act.sa_handler = signal_handler;
	sigemptyset(&act.sa_mask);
	act.sa_flags = 0;

	sigaction(SIGINT, &act, NULL);
	sigaction(SIGQUIT, &act, NULL);
	sigaction(SIGTERM, &act, NULL);
	sigaction(SIGHUP, &act, NULL);

	act.sa_handler = sigusr2_handler;
	sigaction(SIGUSR2, &act, NULL);
}

#endif

ECMBOOL
ecm_signal_init(void)
{
	sig_mutex = ecm_mutex_create(ECM_FALSE);
#ifndef _WIN32
	setup_handler();
#else
	SetConsoleCtrlHandler(CtrlHandler, TRUE);
#endif
	return ECM_TRUE;
}

